﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LucyGrid
{
	/// <summary>
	/// Performance counters helper
	/// <remarks>Allows to get windows performance counters values.
	/// Returns information about the performance limits allowed for a grid node to work.
	/// </remarks>
	/// </summary>
	public class GridPerformance
	{
		/// <summary>
		/// Last processor measures
		/// </summary>
		public static float[] ProcessorSamples { get; set; }
		/// <summary>
		/// Last processor average
		/// </summary>
		public static float ProcessorAvg { get; protected set; }
		/// <summary>
		/// Max processor usage limit to work.
		/// </summary>
		public static float MaxProcessorUsage { get; set; }
		/// <summary>
		/// Processor offset for restoring grid processing
		/// </summary>
		public static float MaxProcessorUsageOffset { get; set; }

		/// <summary>
		/// Last memory measures
		/// </summary>
		public static float[] MemorySamples { get; set; }
		/// <summary>
		/// Last memory average
		/// </summary>
		public static float MemoryAvg { get; protected set; }
		/// <summary>
		/// Max. memory limit
		/// </summary>
		public static float MaxMemoryUsage { get; set; }
		/// <summary>
		/// Memory offset for restoring grid processing
		/// </summary>
		public static float MaxMemoryUsageOffset { get; set; }
		/// <summary>
		/// Numver of measures to calculate the avergae
		/// </summary>
		public static int avgTrend = 10;


		/// <summary>
		/// Keeps the counter update running
		/// </summary>
		static bool run;
		/// <summary>
		/// Counter update task
		/// </summary>
		static Task runner;
		public static bool Pause;


		static GridPerformance()
		{
			ProcessorAvg = 0;
			MemoryAvg = 0;
			MaxProcessorUsage = 80;
			MaxMemoryUsage = 75;
			MaxProcessorUsageOffset = 20;
			MaxMemoryUsageOffset = 10;
			ProcessorSamples = new float[avgTrend];
			MemorySamples = new float[avgTrend];
			Pause = false;
		}

		/// <summary>
		/// Starts capturing counters
		/// </summary>
		public static void StartSampling() {
			run = true;
			runner = Task.Run(() =>
			{
				Thread.CurrentThread.Name = string.Format("GRIDPERFORMANCE SAMPLING-{0}", Thread.CurrentThread.ManagedThreadId);
				PerformanceCounter cpuCounter;
				cpuCounter = new PerformanceCounter();
				cpuCounter.CategoryName = "Processor";
				cpuCounter.CounterName = "% Processor Time";
				cpuCounter.InstanceName = "_Total";

				PerformanceCounter ramCounter = new PerformanceCounter();

				ramCounter.CounterName = "% Committed Bytes In Use";
				ramCounter.CategoryName = "Memory";
				var i = 0;
				// Initialize
				cpuCounter.NextValue();
				cpuCounter.NextValue();

				ProcessorAvg = 0;
				MemoryAvg = 0;
				do
				{
					ProcessorSamples[i] = cpuCounter.NextValue();
					MemorySamples[i] = ramCounter.NextValue();
					if (++i == ProcessorSamples.Length) {
						ProcessorAvg = ProcessorSamples.Average();
						MemoryAvg = MemorySamples.Average();
						i = 0;
					}
					Thread.Sleep(5000);
				} while (run);
			});
		}

		/// <summary>
		/// Stops capturing task
		/// </summary>
		public static void StopSampling() {
			run = false;
			runner.Wait();
		}

		/// <summary>
		/// Returns the number of cores available in the machine
		/// </summary>
		/// <returns></returns>
		public static int CoresCount()
		{
			return Environment.ProcessorCount;
		}

		/// <summary>
		/// Indicates if the max working limits has been reached
		/// </summary>
		/// <returns></returns>
		public static bool IsHighLimitReached()
		{
			return ProcessorAvg >= MaxProcessorUsage ||
				MemoryAvg >= MaxMemoryUsage;
		}

		/// <summary>
		/// Indicates if we are able to start working again
		/// </summary>
		/// <returns></returns>
		public static bool IsLowLimitReached()
		{
			return ProcessorAvg <= (MaxProcessorUsage - MaxProcessorUsageOffset) &&
				MemoryAvg <= (MaxMemoryUsage - MaxMemoryUsageOffset);
		}

		/// <summary>
		/// Best number of threads per this machine
		/// </summary>
		public static int NumberOfThreadsPerMachine
		{
			get
			{
				return Environment.ProcessorCount * 4;
			}
		}
	}
}
